package ru.dc;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import ru.dc.object.CommandListener;
import ru.dc.object.DownloadItem;
import ru.dc.object.DownloadSource;
import ru.dc.object.ServerInfo;
import ru.dc.object.commands.*;

import java.io.BufferedOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.Socket;

/**
 * Created by IntelliJ IDEA.
 * User: deady
 * Date: 04.11.2010
 * Time: 0:04:25
 */
public class IncomingClientConnection extends ClientConnection {

    private Socket socket;
    private Log logger = LogFactory.getLog(getClass());

    public IncomingClientConnection(Socket socket) {
        this.socket = socket;
        ServerInfo si = new ServerInfo();
        si.setAddress(socket.getInetAddress().getHostAddress());
        si.setPort(socket.getPort());
        setServerInfo(si);//fake serverInfo to pass connect()
    }

    @Override
    protected Socket createSocket() throws IOException {
        return socket;
    }

    @Override
    protected void beforeConnect() {
        CommandDispatchingThread.getInstance().addCommandListener(new CommandListener() {
            public void commandRecieved(Command command) {

                if (command.getConnection()!=IncomingClientConnection.this) {
                    return;
                }

                if (command instanceof MyNick) {
                    MyNick myNick = (MyNick) command;
                    setRemoteNick(myNick.getNick());

                    //получили ник клиента на удаленной стороне. теперь необходимо узнать
                    //на каком хабе зарегистрирован клиент, чтобы однозначно понять
                    //какие файлы нужно с него качать.
                    //так как на других хабах могут быть клиенты с таким же именем,
                    //способ не надежный.
                    //например, на хабе hub1 есть клиент foo. но на хабе 2 есть
                    //другой клиент с таким же именем foo, и мы хотим скачать с
                    //клиента foo с хаба 2 файл file1.
                    //пусть клиент foo с хаба1 подключился к нам с целью получить список
                    //файлов. в процессе поиска с какого хаба пришел клиент мы увидем, что
                    //такой есть на обоих хабах. логично взять первый попавшийся хаб и назначить
                    //его клиенту. если таким хабом будет хаб 2, то мы ошибочно запросим file1
                    //с этого клиента.
                    ServerInfo hubInfo = Application.getApplication().getConnectionManager().findUser(getRemoteNick());
                    if (hubInfo==null) {
                        logger.debug("No hub found for user "+getRemoteNick());
                        try {
                            disconnect();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    else {
                        setHubInfo(hubInfo);
                        logger.debug("Assigned hub to user "+getRemoteNick()+": "+getHubInfo());
                    }

                }

                else if (command instanceof Lock) {

                    Lock lockCmd = (Lock) command;
                    String lockStr = lockCmd.getLock();
                    setKey(Lock.convertLockToKey(lockStr));

                    final MyNick nick = new MyNick();
                    nick.setNick(getNick());
                    sendCommand(nick);

                    Lock lock = new Lock();
                    lock.setLock(lockStr);
                    lock.setPk(lockCmd.getPk());
                    sendCommand(lock);


                    if (lockCmd.isSupportsExtended()) {
                        Supports supports = new Supports();
                        supports.setFeatures("MiniSlots XmlBZList ADCGet TTHL TTHF GetZBlock ZLIG");
                        sendCommand(supports);
                    }


                    DownloadSource source = new DownloadSource();
                    source.setNick(getRemoteNick());
                    source.setHub(getHubInfo());
                    DownloadItem i = Application.getApplication().getDownloadQueue().findNextDownload(source);

                    Direction direction = new Direction();
                    direction.setOperation(i!=null ? Direction.Constants.DOWNLOAD : Direction.Constants.UPLOAD);
                    setLocalDirectionNumber(direction.getRandom());
                    sendCommand(direction);

                    Key key = new Key();
                    key.setKey(getKey());
                    sendCommand(key);


                }


                else if (command instanceof Direction) {

                    Direction direction = (Direction) command;
                    setRemoteDirectionNumber(direction.getRandom());
                    setRemoteOperation(direction.getOperation());



                }


                else if (command instanceof Key) {//Последняя команда в приветствии
                    //проверяем, имеем ли мы возможность начать загрузку первыми
                    if (getRemoteOperation().equals(Direction.Constants.UPLOAD) || getRemoteDirectionNumber()<getLocalDirectionNumber()) {
                        //если да, то запрашиваем очередной файл для загрузки
                        startDownloadingItems();
                    }
                    else {
                        logger.warn("Loose download number battle");
                    }
                }

                else if (command instanceof FileLength) {
                    FileLength length = (FileLength) command;
                    getCurrentDownloadItem().setLength(length.getLength());
                    logger.trace("Got length for "+ getCurrentDownloadItem().getFilename()+": "+length.getLength());

                    Send send = new Send();
                    setWaitingRawData(true);

                    Application.getApplication().getDownloadQueue().markActive(getCurrentDownloadItem());



                    if (!getTargetFile().exists()) {
                        try {
                            getTargetFile().createNewFile();
                        } catch (IOException e) {
                            logger.error("Can't create file "+getTargetFile().getAbsolutePath(), e);
                        }
                    }

                    try {
                        setOut(new BufferedOutputStream(new FileOutputStream(getTargetFile(), true)));
                    } catch (FileNotFoundException e) {
                        logger.error("Can't create output stream for "+getTargetFile().getAbsolutePath(), e);
                    }

                    sendCommand(send);
                }
            }
        });
    }


    @Override
    protected void afterConnect() {
    }
}
